package game;

import heroes.Hero;
import heroes.Skill;
import heroes.TestHero;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;

import javax.swing.JPanel;

import cards.Attack;
import cards.BaGua;
import cards.BaiYin;
import cards.Basic;
import cards.BinLiang;
import cards.Card;
import cards.CiXiong;
import cards.Dodge;
import cards.FangTian;
import cards.FireAttack;
import cards.GuDian;
import cards.GuanShi;
import cards.GuoHe;
import cards.HanBin;
import cards.Heal;
import cards.HorseMinus;
import cards.HorsePlus;
import cards.HuoGong;
import cards.JieDao;
import cards.JueDou;
import cards.LeBu;
import cards.Magic;
import cards.NanMan;
import cards.QiLin;
import cards.QinGang;
import cards.QingLong;
import cards.Range;
import cards.RengWang;
import cards.ShanDian;
import cards.ShunShou;
import cards.Spirit;
import cards.TaoYuan;
import cards.TengJia;
import cards.ThunderAttack;
import cards.TieSuo;
import cards.WanJian;
import cards.WuGu;
import cards.WuXie;
import cards.WuZhong;
import cards.ZhangBa;
import cards.ZhuGe;
import cards.ZhuQue;

public class View extends JFrame {
	public View(Model gameModel) {
		myModel = gameModel;
		setTitle(myModel.myPlayerName + "'s game");
		setLayout(new BorderLayout());
		// setPreferredSize(new Dimension(GAME_VIEW_WIDTH,GAME_VIEW_HEIGHT));
		setSize(new Dimension(GAME_VIEW_WIDTH, GAME_VIEW_HEIGHT));

		CountDown.myView = this;
		myLowerPanel = new LowerPanel(this);
		add(myLowerPanel, BorderLayout.SOUTH);
		// loading images here and tell the right panel
		// recSysMsg("Loading images...");
		myModel.myLoginView.appendMsg("Loading images...");
		loadCardImage();
		// recSysMsg("Loading heros...");
		myModel.myLoginView.appendMsg("Loading heros...");

		loadHeroImage();
		// recSysMsg("all loadings are done");
		myModel.myLoginView.appendMsg("all loadings are done");

		// TODO set up other variables

	}

	public void linkToHero(Hero toBlinked) {
		myHero = toBlinked;
		myLowerPanel.linkToHero();
	}

	// TODO why do i have this shit???
	private Toolkit tk = Toolkit.getDefaultToolkit();

	private void loadHeroImage() {

		tk.getImage("");
		try {
			{
				Hero.HeroBack = ImageIO.read(new File(
						"src/resource/HeroBack.jpg"));
			}
			{// TestHero
				TestHero.ImageInit(ImageIO.read(new File(
						"src/resource/Heros/QunXiong/LvBu.png")));
			}
			{
				// TODO 019:: load all other hero images here
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void loadCardImage() {
		try {
			Card.CardBack = ImageIO.read(new File("src/resource/CardBack.jpg"));
			Card.unclickableWaterPrint = ImageIO.read(new File(
					"src/resource/UnclickableWaterPrint.png"));
			{// basic
				Attack.ImageInit(ImageIO.read(new File(
						"src/resource/Basic/Attack.jpg")));
				Dodge.ImageInit(ImageIO.read(new File(
						"src/resource/Basic/Dodge.jpg")));
				FireAttack.ImageInit(ImageIO.read(new File(
						"src/resource/Basic/FireAttack.jpg")));
				Heal.ImageInit(ImageIO.read(new File(
						"src/resource/Basic/Heal.jpg")));
				Spirit.ImageInit(ImageIO.read(new File(
						"src/resource/Basic/Spirit.jpg")));
				ThunderAttack.ImageInit(ImageIO.read(new File(
						"src/resource/Basic/ThunderAttack.jpg")));
			}
			{// armor
				BaGua.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Armor/BaGua.jpg")));
				BaiYin.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Armor/BaiYin.jpg")));
				RengWang.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Armor/RengWang.jpg")));
				TengJia.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Armor/TengJia.jpg")));
			}
			{// horse
				HorsePlus.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Horse/Horse.jpg")));
				HorseMinus.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Horse/Horse1.jpg")));
			}
			{// weapon
				CiXiong.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Weapon/CiXiong.jpg")));
				FangTian.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Weapon/FangTian.jpg")));
				GuanShi.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Weapon/GuanShi.jpg")));
				GuDian.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Weapon/GuDian.jpg")));
				HanBin.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Weapon/HanBin.jpg")));
				QiLin.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Weapon/QiLin.jpg")));
				QinGang.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Weapon/QinGang.jpg")));
				QingLong.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Weapon/QingLong.jpg")));
				ZhangBa.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Weapon/ZhangBa.jpg")));
				ZhuGe.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Weapon/ZhuGe.jpg")));
				ZhuQue.ImageInit(ImageIO.read(new File(
						"src/resource/Equipment/Weapon/ZhuQue.jpg")));
			}
			{// magic
				BinLiang.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/BinLiang.jpg")));
				GuoHe.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/GuoHe.jpg")));
				TaoYuan.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/TaoYuan.jpg")));
				HuoGong.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/HuoGong.jpg")));
				JieDao.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/JieDao.jpg")));
				JueDou.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/JueDou.jpg")));
				LeBu.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/LeBu.jpg")));
				NanMan.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/NanMan.jpg")));
				ShanDian.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/ShanDian.jpg")));
				ShunShou.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/ShunShou.jpg")));
				TieSuo.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/TieSuo.jpg")));
				WanJian.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/WanJian.jpg")));
				WuGu.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/WuGu.jpg")));
				WuXie.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/WuXie.jpg")));
				WuZhong.ImageInit(ImageIO.read(new File(
						"src/resource/Magic/WuZhong.jpg")));
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void initRightPanel() {
		// playerBox initialization is done by the constructor of RightPanel
		myRightPanel = new RightPanel(this);
		// after rightPanel is done, all messages can be sent to rightPanel
		// instead of LoginView
		myModel.myLoginView.active = false;
		add(myRightPanel, BorderLayout.EAST);
	}

	public void initBattleField() {
		for (Hero every : myModel.heroList) {
			every.initMyDisplayer();
		}
		myBattleField = new BattleField(this);
		add(myBattleField, BorderLayout.CENTER);
	}

	// these two variables will never change throughout the game.
	public static int GAME_VIEW_WIDTH;
	public static int GAME_VIEW_HEIGHT;

	public static void initPreferredSize() {

		GAME_VIEW_WIDTH = LoginView.LOGINVIEW_DIMENSION.width;
		GAME_VIEW_HEIGHT = LoginView.LOGINVIEW_DIMENSION.height;
		System.out.println("GAME_VIEW_WIDTH " + GAME_VIEW_WIDTH);
		System.out.println("GAME_VIEW_HEIGHT " + GAME_VIEW_HEIGHT);

		// the init of three buttons: ok, end, cancel. are moved into
		// LowerPanel.
	}

	BattleField myBattleField;
	public LowerPanel myLowerPanel;
	RightPanel myRightPanel;

	Controller myController;
	public Model myModel;

	// TODO this will has a little space showing how many
	// cards are left in the deck.

	// @deprecated: moved to the Model
	// Player myPlayer;
	Hero myHero;
	public static int OK_X;
	public static int OK_Y;
	public static int CANCEL_X;
	public static int CANCEL_Y;
	public static int END_X;
	public static int END_Y;

	JButton OK = new JButton("OK");
	JButton Cancel = new JButton("Cancel");
	JButton End = new JButton("End");
	boolean okEnabled;
	boolean cancelEnabled;
	boolean endEnabled;

	private void enableOK() {
		okEnabled = true;
		OK.setEnabled(true);
	}

	private void enableCancel() {
		cancelEnabled = true;
		Cancel.setEnabled(true);
	}

	private void enableEnd() {
		endEnabled = true;
		End.setEnabled(true);
		End.revalidate();
		End.repaint();
	}

	private void disableOK() {
		okEnabled = false;
		OK.setEnabled(false);
	}

	private void disableCancel() {
		cancelEnabled = false;
		Cancel.setEnabled(false);
	}

	private void disableEnd() {
		endEnabled = false;
		End.setEnabled(false);
	}

	public static int SPEC_X;
	public static int SPEC_Y;
	public static int SPEC_WIDTH;
	public static int SPEC_HEIGHT;
	public static int SPEC_EDGE;

	// 043, the only reason i keep a list of special skill buttons is
	// because I
	// want to enable the right one when needed. I think I can do all this in
	// the newly created Skill class. just link buttons with skills when I
	// create those buttons, so Skill objects know what to enable/disable

	// @deprecated. moved into class Skill
	// public void enableSpecButton(int i) {
	// specEnabled[i] = true;
	// spec[i].setEnabled(true);
	// }

	public void initButtons() {
		// IMPORTANT, even though these buttons are in View, their position
		// should be in LowerPanel.myLowerRightPanel. And their display should
		// also be manged by myLowerRightPanel.
		// first set button's location

		OK.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				try {
					OK();
				} catch (InterruptedException e1) {
					recSysMsg("OK button failure");
					e1.printStackTrace();
				}

			}
		});
		Cancel.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				try {
					cancel();
				} catch (InterruptedException e1) {
					recSysMsg("Cancel button failure");
					e1.printStackTrace();
				}

			}
		});
		End.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				end();
			}
		});
	}

	private void disableAllButtons() {
		// disable special skill buttons
		for (Skill each : myHero.skills) {
			each.disable();
		}
		// disable other buttons
		disableCancel();
		disableOK();
		disableEnd();
	}

	public void initSkillButtons() {
		for (final Skill every : myHero.skills) {
			JButton skillButton = new JButton(every.getName());
			every.link(skillButton);
			every.disable();
			// linking active skill events
			if (every.isActiveSkill()) {
				skillButton.addActionListener(new ActionListener() {
					@Override
					public void actionPerformed(ActionEvent e) {
						if (every.isEnabled()) {
							every.chosen = true;
							disableAllButtons();
							every.skillProcessInit();
						}
					}
				});
				myLowerPanel.skillPanel.add(skillButton);
			}
		}
	}

	public static enum Action {
		// actions have been done.
		Dodge, DrawCard, WuXie, Attack, Save, DrinkSpirit, Die, Discard, Move, ChangePandin, ReplacePandin,
		//
		BinLiang, LeBu, ShanDian,
		//
		GuoHe, GuoHeConfirm, ShunShou, ShunShouConfirm, //

		// Heal and Save will be two diff thing
		Heal, //

		// TODO tons of actions to be added
		// one thing interesting, FireAttack will be under the class of Attack.
		// The fire attribute is added in the hero's attack()
		ZhangBaAttack, ZhuQueAttack, CiXiong, CiXiongResponse,

		JieDao, JieDaoConfirm, //
		DiscardHandCard, //
		NanMan, NanManConfirm, WanJian, WanJianConfirm, EquipWeapon, EquipArmor, EquipHorsePlus, EquipHorseMinus,
		// @deprecated, @see Action.Skill
		// // in this game, no one has more than three skills.
		// Spec1, Spec2, Spec3
		Skill, WuGu, WuGuConfirm, JueDou, JueDouConfirm, WuZhong, TaoYuan, JieDao2, TieSuo, HuoGong, HuoGongConfirm1, HuoGongConfirm2, BaGua, ZhuQue
	}

	Action currentAction = null;
	// A VERY IMPORTANT BOOLEAN HERE: TO SHOW WHETHER WE ARE ACTING ON OUR
	// OWN OR NOT
	boolean requiredFromOutside;

	// @deprecated, this is now moved to LowerPanel/ BattleField
	// WHEN REQUIRED == 0, THE REQUIRE NUMBER IN BATTLEFIELD AND LOWERPANEL
	// WILL BE 0

	public void checkReady() {
		if (myLowerPanel.ready && myBattleField.ready) {
			enableOK();
		} else {
			disableOK();
		}
	}

	// this will be notified from our lowerPanel/lowerCardContainer or skill
	// button.
	// then sets the view in required mode, so we can indicate what other
	// places we need to click in battlefield and LowerPanel
	// UPDATE: this part is took away because the firstChoiceStart is now
	// handled by the LowerPanel, View only detects what process is started and
	// trigger corresponding methods.
	// public void firstChoiceMade() {
	//
	// }

	// @deprecated: if we put our line here and use the view's line to
	// communicate with the server, we jump over the process of Hero and Model,
	// this may cause some skill triggering being skipped.
	// /**
	// * this line is for communication reason.
	// *
	// * the OK() will fulfill this line with informations, and xxxProcessEnd()
	// * will send this line to server;
	// */
	// String line = "";

	// public void setLine(String toBset) {
	// synchronized (line) {
	// line = toBset;
	// }
	// }

	// public void sendLine() {
	// // we first send it to the model and then the model send it out
	// // because the model knows i am a client or a server
	// synchronized (line) {
	// myModel.sendLine(line);
	// // after sending clear the line for further messages
	// line = "";
	// }
	// }

	// cannot be null, otherwise we cannot lock it

	// in case of misuse, all communications go UPPERCASE
	public void OK() throws InterruptedException {
		System.out.println("okEnabled == " + okEnabled);

		/*
		 * IMPORTANT no matter where we use line, make sure we lock it. ex. we
		 * may chat while send a Game information at the same time then the line
		 * += both informations from two sources and cause unpredictable
		 * problems
		 */
		if (okEnabled) {
			System.out.println("ok()");
			// disable OK after clicking OK
			disableAllButtons();
			myBattleField.clearRequirements();
			myLowerPanel.clearRequirements();
			myBattleField.requireClickableSquares();
			myLowerPanel.requireClickableCards();
			// line += "G.";
			if (requiredFromOutside == false) {
				switch (currentAction) {
				case Move:
					moveProcessEnd();
					break;
				case Attack:
					attackProcessEnd();
					break;
				// case Dodge:
				// dodgeProcessEnd();
				// break;
				case Heal:
					healProcessEnd();
					break;
				case DrinkSpirit:
					drinkSpiritProcessEnd();
					break;
				case Discard:
					discardProcessEnd();
					break;
				case ChangePandin:
					// TODO
					break;
				case ReplacePandin:
					// the difference is shown only when we send the msg to the
					// server
					pandinChangeProcessEnd();
					break;
				case BinLiang:
					BinLiangProcessEnd();
					break;
				case LeBu:
					LeBuProcessEnd();
					break;
				case ShanDian:
					ShanDianProcessEnd();
					break;
				case GuoHe:
					guoHeProcessEnd();
					break;
				case ShunShou:
					shunShouProcessEnd();
					break;
				case Skill:
					for (Skill every : myHero.skills) {
						if (every.chosen) {
							every.chosen = false;
							every.skillProcessEnd();
							break;
						}
					}
					myRightPanel.appendSysMsg("ERROR:: View.OK().case Skill:");

				case CiXiong:
					ciXiongProcessEnd();
					break;
				case EquipWeapon:
					equipWeaponProcessEnd();
					break;
				case EquipArmor:
					equipArmorProcessEnd();
					break;
				case EquipHorsePlus:
					equipHorsePlusProcessEnd();
					break;
				case EquipHorseMinus:
					equipHorseMinusProcessEnd();
					break;
				case WuGu:
					wuGuProcessEnd();
					break;
				case JueDou:
					jueDouProcessEnd();
					break;
				case WuZhong:
					wuZhongProcessEnd();
					break;
				case WanJian:
					wanJianProcessEnd();
					break;
				case NanMan:
					nanManProcessEnd();
					break;
				case TaoYuan:
					taoYuanProcessEnd();
					break;
				case JieDao:
					jieDaoProcessEnd();
					break;
				case JieDao2:
					jieDaoProcessEnd2();
					break;
				case TieSuo:
					tieSuoProcessEnd();
					break;
				case HuoGong:
					huoGongProcessEnd();
					break;
				}
			} else {// requiredFromOutside == true
				/*
				 * if required from outside, we need to ensure that the
				 * requirement is answered first.
				 */
				// line += "Y";
				switch (currentAction) {
				/*
				 * IMPORTANT,主动的时候，会在firstChoiceStart()处 设置好currentAction.
				 * 被动的时候，会在被要求的时候设置好currentAction。
				 * 
				 * 如此，才能保证武将技能总是会触发 武将技能的处发点是ProcessEnd那里� 岱祷馗鳰odel关于武将目前的信息
				 * 包括技能/装备/武器等 然后Model判断会发生什么
				 * 
				 * 比如公孙激昂： 敌人打出红色“杀”，动作是“杀”。公孙确定并打出闪， 会有两条信息： 1.告知主机YES和杀的颜色等等，
				 * 2.(主机测试杀 的颜色，并告知每一个玩家打出了什么牌)自己的武 将Hero处触发新的消息，
				 * 这两个消息是会有先后顺序之分的， 因为敌人打出红色杀，首先是主机广播红色杀消息。 然后公孙瓒回应了这个广播，发出摸排消息。
				 * 然后才是主机要求公孙出闪
				 * 
				 * 这样的模型下，需要完善好Hero和View。 另外，Client不需要存� ⑵溆郒ero他不需要知道的信息，比如手牌内容
				 * 只需要记录手牌数量。这些信息会存。储在Model里面 ，我们仍然需要model，不然不会知道谁是当前的出牌者。
				 * 
				 * 这个模型下，要求Hero要有完善的接口，方便子类的创建
				 */
				case Attack:
					attackProcessEnd();
					break;
				case Dodge:
					dodgeProcessEnd();
					break;
				case Heal:
					break;
				case ChangePandin:

					break;
				case Save:
					saveProcessEnd();
					break;
				case WuXie:
					wuXieProcessEnd();
					break;
				case CiXiongResponse:
					ciXiongResponseProcessEnd();
					break;
				case JueDouConfirm:
					jueDouConfirmEnd();
					break;
				case WanJianConfirm:
					wanJianConfirmEnd();
					break;
				case NanManConfirm:
					nanManConfirmEnd();
					break;
				case JieDaoConfirm:
					jieDaoConfirmProcessEnd();
					break;
				case HuoGongConfirm1:
					huoGongConfirm1End();
					break;
				case HuoGongConfirm2:
					huoGongConfirm2End();
					break;
				case BaGua:
					baGuaProcessEnd();
					break;
				}

			}
			clearChosenCards();
		} else {
			System.err.println("OK() called while Ok is not enabled");
		}
	}

	protected void clearChosenCards() {
		myLowerPanel.clearChosenCards();
	}

	/*
	 * to be changed a lot. cancel out any require situation
	 */
	public void cancel() throws InterruptedException {
		// here should be a lock. as i said before, we need lock for the
		// view.
		// if(lock.isLocked){
		// return;
		// }
		// lock.lock();
		// synchronized (line) {
		if (cancelEnabled) {
			disableAllButtons();
			myBattleField.clearRequirements();
			myLowerPanel.clearRequirements();
			myBattleField.requireClickableSquares();
			myLowerPanel.requireClickableCards();
			if (requiredFromOutside == false) {
				currentAction = null;
				// clean up everything
				myLowerPanel.cancel();
				myBattleField.cancel();
				okEnabled = false;
				if (!playStageFinished) {
					playReadyState();
				} else {
					discardProcessInit();
				}
			} else {// requiredFromOutside == true
				myLowerPanel.cancel();
				myBattleField.cancel();
				disableOK();
				myModel.sayNoToAction(currentAction.toString());
				if(currentAction== Action.CiXiong||currentAction==Action.ZhuQue){
					weaponSkillLock.unlock();
				}
				// myModel.sendLine("G.N." + currentAction.toString());
				currentAction = null;

			}
		}
		// lock.unlock();
	}

	public void end() {
		System.out.println("end()");
		if (endEnabled) {
			disableAllButtons();
			// disable all cards and grids
			myBattleField.clearRequirements();
			myLowerPanel.clearRequirements();
			myBattleField.requireClickableSquares();
			myLowerPanel.requireClickableCards();

			if (requiredFromOutside == false) {
				myModel.signalPlayStageEnd();
				// myModel.sendLine("G.PLAYSTAGEEND");
				// @deprecated, no need for that, the server will send
				// "G.PLAYSTAGEEND." + userName back and the client will starts
				// on discardProcessInit() once receives the msg.
				// write a line to the server
				// discardProcessInit();
			}
		}
	}

	public void timesUp() {
		System.out.println("View.timesUp()");
		disableAllButtons();
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		myBattleField.requireClickableSquares();
		myLowerPanel.requireClickableCards();
		// NOTICE:: do not send cancel() msg, because the timer on server will
		// time out and assume you sent a cancel() already.
		validate();
		repaint();
	}

	public void removeMyCard(Card toB) {
		myLowerPanel.removeMyCard(toB);
	}

	/**
	 * 
	 * @return if we need to wait for Pandin to be processed by server.
	 */
	public void checkPandin() {
		myHero.checkPandin();
	}

	public void drawReadyState() {

	}

	// boolean playReadyState = false;

	public void playReadyState() {
		if (myHero.canPlayCard) {
			requiredFromOutside = false;
			// playReadyState = true;
			disableCancel();
			disableOK();
			// send msg for playStageEnd;
			enableEnd();
			myLowerPanel.playReadyState();
			myBattleField.playReadyState();
			// if u play ur card, the counting is stopped, and reset to 30 sec,
			// waiting for you to play a new card.
			// if the coungting is finished, regard u as choosing to end your
			// play stage.

			// skill buttons should also be in Hero.playReadyState()
			myHero.playReadyState();
		} else {
			myModel.signalPlayStageEnd();
		}
	}

	public void discardProcessInit() {
		if (myHero.canDiscardCard && myHero.discardNum() != 0) {
			requiredFromOutside = false;
			// TODO 051
			currentAction = Action.Discard;
			enableCancel();
			disableOK();
			disableEnd();
			myBattleField.discardReadyState();
			myLowerPanel.discardReadyState();
			// 052 should i call revalidate()???????
			// revalidate();
			invalidate();
			repaint();
		} else {
			myModel.signalDiscardStageEnd();
		}
	}

	private void discardProcessEnd() {
		currentAction = null;
		ArrayList<Card> discardedCard = myLowerPanel.chosenCards;
		myHero.discardCard(discardedCard);
	}

	public void discardStage() {

	}

	// TODO?? what is this method supposed to do? Deprecated?
	// Bagua. QingLong.
	public void requireEquipCardString(String required) {

	}

	// TODO needs to be revised.
	public void displayCard() {
		PopoutDialog temp = new PopoutDialog(null, "Card displaying");
	}

	// this method inherits from the class Container. Not sure about the
	// reliability of this method.
	public void paintComponents(Graphics arg0) {
		// first paint the basic UI.
		super.paintComponents(arg0);
		// myBattleField will be added, so the super.paintComponent will handle
		// it.
	}

	// lock应该在每个按键的开头都使用上，这样就不会出现连续点击ok出的问题。
	public Lock lock = new Lock();
	boolean playStageFinished = false;
	boolean discardStageFinished = false;

	// IMPORTANT when it's your turn, don't call playerRoundChange(), just call
	// this method, this will be indicated by the Server's broadcast.
	public void intoMyRound() {
		System.out.println("View.intoMyRound");
		try {
			playStageFinished = false;
			discardStageFinished = false;
			myBattleField.clearRequirements();
			myLowerPanel.clearRequirements();
			myHero.statusClear();
			lock.lock();
			beforePandin();
			lock.lock();
			checkPandin();
			lock.lock();
			drawCard();
			// play stage
			// wait for the lock to unlock itself.
			do {
				lock.lock();
				// send a msg to server, and start counting 30 sec
				System.out.println("myHero.signalPlayStageStart();");
				myHero.signalPlayStageStart();
				myBattleField.clearRequirements();
				myLowerPanel.clearRequirements();
				System.out.println("playReadyState();");
				playReadyState();
				lock.lock();
				// playReadyState = false;
				lock.unlock();
				System.out.println("playStageFinished " + playStageFinished);
			} while (!playStageFinished);
			// IMPORTANT, if 30 sec passed, the timer times out while we
			// just send a
			// msg to the server. the server will ignore it and the msg
			// lock
			// will
			// keep locked because no feedback is received.
			// We unlock the msgLock here so there is no dead lock.
			// Furthermore, the
			// msg we send will be ignored because of answeree/
			// answeraction
			// check.

			// myModel.msgLock.unlock();

			myModel.releaseAllLocks();

			// discard stage
			do {

				lock.lock();
				myModel.discardLock.lock();
				System.out.println("lock.lock()");
				// send a message to server that I started.
				myHero.signalDiscardStageStart();
				myBattleField.clearRequirements();
				myLowerPanel.clearRequirements();
				discardProcessInit();
				lock.lock();
				System.out.println("lock.lock()");
				lock.unlock();
				System.out.println("lock.unlock()");
				System.out.println("discardStageFinished "
						+ discardStageFinished);
			} while (!discardStageFinished);
			myModel.releaseAllLocks();
			// lock.lock();
			lock.lock();
			afterDiscard();
			lock.lock();
			lock.unlock();
			myModel.signalRoundChange();
			myHero.roundEnd();
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

	}

	private void drawCard() {
		myHero.drawCard();
	}

	private void beforePandin() {
		// trigger special hero skills here.
		myHero.beforePandin();
	}

	private void afterDiscard() {
		// trigger special hero skills here.
		myHero.afterDiscard();
		// disable all cards, fields and buttons
	}

	// @deprecated, i think everything can be done in intoMyRound();
	// public void outOfMyRound() {
	//
	// }

	// the player activated in battle field will change,
	// so the color will change too.
	// also the right panel player list.
	public void playerRoundChange() {
		recSysMsg("ROUND CHANGE");
		clearProgress();
		disableAllButtons();
		myBattleField.playerRoundChange();
		myRightPanel.playerRoundChange();
		{// changing myModel.currentPlayer
			myModel.currentNum = (myModel.currentNum + 1) % myModel.totalNum;
			myModel.currentPlayer = myModel.playerList.get(myModel.currentNum);
			recSysMsg("It's " + myModel.currentPlayer.getName()
					+ "'s turn now.");
		}
	}

	// the following public variable is for the use of memorizing the card we
	// choose.
	// for the sake of color and number.
	// IMPORTANT, because there are three kinds of attack, so we cannot use
	// Attack class as the type of our attackCard
	// UPDATE , we may not need to memorize the card here,
	// because this information will be stored in the LowerCardContainer
	// we can retrieve it later.
	// public Basic attackCard;

	public void attackProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		// attackCard = card;
		currentAction = Action.Attack;
		requiredFromOutside = false;
		// Fang Tian skill here
		if (myHero.handCardNum == 1 && myHero.weaponEquipped
				&& myHero.weapon.getName().equals("FangTian")) {
			myBattleField.setRequiredNum(1, 3);
		} else {
			myBattleField.setRequiredNum(1);
		}

		myBattleField.setRequiredDistance(BattleField.weapon_range);
		myBattleField.setRequireEmpty(false);
		myBattleField.myselfClickable = false;
		myBattleField.requireClickableSquares();
		enableCancel();
	}

	public void jueDouProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.JueDou;
		requiredFromOutside = false;
		myBattleField.setRequiredNum(1);
		myBattleField.setRequiredDistance(BattleField.any_range);
		myBattleField.setRequireEmpty(false);
		myBattleField.myselfClickable = false;
		myBattleField.requireClickableSquares();
		enableCancel();
	}

	Lock weaponSkillLock = new Lock();

	public void lockThenUnlockWeaponSkill() {
		try {
			weaponSkillLock.lock();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		weaponSkillLock.unlock();
	}

	public void unlockWeaponSkill() {
		weaponSkillLock.unlock();
	}

	public void ciXiongProcessInit() {
		recSysMsg("Do you want to use your CiXiong weapon?");
		enableCancel();
		currentAction = Action.CiXiong;
		requiredFromOutside = false;
		myBattleField.ready = true;
		myLowerPanel.ready = true;
		checkReady();
		try {
			weaponSkillLock.lock();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void ciXiongProcessEnd() {
		currentAction = null;
		Hero ciXiongTarget = myBattleField.clickedSquares.get(0).occupier;
		myHero.ciXiong(ciXiongTarget);
	}

	public void zhuQueProcessInit() {
		recSysMsg("Do you want to use your ZhuQue weapon?");
		enableCancel();
		currentAction = Action.ZhuQue;
		requiredFromOutside = false;
		myBattleField.ready = true;
		myLowerPanel.ready = true;
		checkReady();
		try {
			weaponSkillLock.lock();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	public void zhuQueProcessEnd() {
		currentAction = null;
		myHero.zhuQue();
	}

	public void ciXiongResponseProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.CiXiongResponse;
		requiredFromOutside = true;
		myBattleField.ready = true;
		enableCancel();
		myLowerPanel.setRequiredNum(1);
		myLowerPanel.requireClickableCards();
	}

	public void ciXiongResponseProcessEnd() {
		currentAction = null;
		Card theCard = myLowerPanel.chosenCards.get(0);
		disableCancel();
		myHero.ciXiongResponse(theCard);
	}

	/**
	 * this method will retrieve information of the attack card from the lower
	 * panel , then retrieve informations of the target from the BattleField
	 * then combine these informations into one line, then send this to the
	 * server via Client.
	 */
	private void attackProcessEnd() {
		currentAction = null;
		Card attackCard = myLowerPanel.chosenCards.get(0);
		myHero.playAttack(attackCard);
		if (myBattleField.clickedSquares.size() == 1) {
			myHero.attack((Basic) myLowerPanel.chosenCards.get(0),
					myBattleField.clickedSquares.get(0).occupier);
		} else {// multi-attack, "Fang Tian"
			ArrayList<Hero> targets = new ArrayList<Hero>();
			for (int i = 0; i < myBattleField.clickedSquares.size(); i++) {
				targets.add(myBattleField.clickedSquares.get(i).occupier);
			}

			myHero.multiAttack((Basic) myLowerPanel.chosenCards.get(0), targets);
		}
		// String toBreturned = null;
		// toBreturned += "ATTACK.";

		// also needs to add up special effects like 雌雄双剑
		// this effect will be inside Hero.attack()

		// toBreturned += myLowerPanel.OK();
		// toBreturned += myBattleField.OK();
		// return toBreturned;
	}

	private void jueDouProcessEnd() {
		currentAction = null;
		Card jueDouCard = myLowerPanel.chosenCards.get(0);
		myHero.playJueDou(jueDouCard);
		myHero.jueDou(myBattleField.clickedSquares.get(0).occupier);
	}

	public void jueDouConfirmInit() {
		disableOK();
		disableEnd();
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		myBattleField.ready = true;
		requiredFromOutside = true;
		enableCancel();
		currentAction = Action.JueDouConfirm;
		myLowerPanel.setRequiredNum(1);
		myLowerPanel.ready = false;
		myLowerPanel.requireFromHand = true;
		myLowerPanel.cardRequiredString.add("Attack");
		myLowerPanel.cardRequiredString.add("FireAttack");
		myLowerPanel.cardRequiredString.add("ThunderAttack");
		myLowerPanel.cardRequired = true;
		myLowerPanel.requireClickableCards();
	}

	public void jueDouConfirmEnd() {
		currentAction = null;
		Card jueDouAttack = myLowerPanel.chosenCards.get(0);
		myHero.playAttack(jueDouAttack);
		myHero.jueDouConfirm();
	}

	public void moveProcessInit() {
		System.out.println("moveProcessInit()");
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.Move;
		requiredFromOutside = false;

		myBattleField.setRequiredNum(1);
		myBattleField.setRequiredDistance(BattleField.horse_range);
		myBattleField.setRequireEmpty(true);
		myBattleField.myselfClickable = false;
		myBattleField.requireClickableSquares();
		enableCancel();
	}

	public void moveProcessEnd() {
		System.out.println("moveProcessEnd()");
		currentAction = null;
		Card moveCard = myLowerPanel.chosenCards.get(0);
		Square targetSquare = myBattleField.clickedSquares.get(0);
		myHero.playDodge(moveCard);
		myHero.move(targetSquare);
	}

	// @deprecated, lock already did this for me.
	// public void waitForActionEnd(){
	// lock.lock();
	//
	// lock.lock();
	// lock.unlock();
	// }

	public void dodgeProcessInit() {
		disableOK();
		disableEnd();
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		myBattleField.ready = true;
		requiredFromOutside = true;
		enableCancel();
		currentAction = Action.Dodge;
		myLowerPanel.setRequiredNum(1);
		myLowerPanel.ready = false;
		myLowerPanel.requireFromHand = true;
		myLowerPanel.cardRequiredString.add("Dodge");
		myLowerPanel.cardRequired = true;
		myLowerPanel.requireClickableCards();
	}

	public void wanJianConfirmInit() {
		disableOK();
		disableEnd();
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		myBattleField.ready = true;
		requiredFromOutside = true;
		enableCancel();
		currentAction = Action.WanJianConfirm;
		myLowerPanel.setRequiredNum(1);
		myLowerPanel.ready = false;
		myLowerPanel.requireFromHand = true;
		myLowerPanel.cardRequiredString.add("Dodge");
		myLowerPanel.cardRequired = true;
		myLowerPanel.requireClickableCards();
	}

	public void dodgeProcessEnd() {
		currentAction = null;
		Card dodgeCard = myLowerPanel.chosenCards.get(0);
		myHero.playDodge(dodgeCard);
		myHero.dodge();
	}

	public void wanJianConfirmEnd() {
		currentAction = null;
		Card dodgeCard = myLowerPanel.chosenCards.get(0);
		myHero.playDodge(dodgeCard);
		myHero.wanJianConfirm();
	}

	public void wuXieProcessInit() {
		// set up buttons
		disableOK();
		enableCancel();
		//
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		myBattleField.ready = true;
		requiredFromOutside = true;
		currentAction = Action.WuXie;
		myLowerPanel.setRequiredNum(1);
		myLowerPanel.ready = false;
		// DONE
		// there should be a method to clear all requirement of the
		// lowerPanel and the battlefield
		myLowerPanel.requireFromHand = true;
		myLowerPanel.cardRequired = true;
		myLowerPanel.cardRequiredString.add("WuXie");
		myLowerPanel.requireClickableCards();
	}

	public void watchWuxie() {
		// is someone played wuxie, disable all buttons.
		disableOK();
		disableCancel();
		// i use this method only for convenience.
		myBattleField.discardReadyState();
		myLowerPanel.cancel();
		myBattleField.clearRequirements();
	}

	public void wuXieProcessEnd() {
		currentAction = null;
		Card wuXieCard = myLowerPanel.chosenCards.get(0);
		myHero.playWuXie(wuXieCard);
		myHero.wuXie();
	}

	public void guoHeProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.GuoHe;
		requiredFromOutside = false;
		myBattleField.setRequiredNum(1);
		myBattleField.setRequiredDistance(BattleField.any_range);
		myBattleField.setRequireEmpty(false);
		myBattleField.myselfClickable = false;
		myBattleField.requireClickableSquares();
		enableCancel();
	}

	private void guoHeProcessEnd() {
		currentAction = null;
		Card GuoHeCard = myLowerPanel.chosenCards.get(0);
		myHero.playGuoHe(GuoHeCard);
		myHero.guoHe(GuoHeCard, myBattleField.clickedSquares.get(0).occupier);
	}

	/**
	 * this will start the CardChoosingDlg
	 * 
	 * @param targetName
	 */
	public void guoHeConfirmInit(String targetName) {
		disableAllButtons();
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		myBattleField.requireClickableSquares();
		myLowerPanel.requireClickableCards();
		currentAction = Action.GuoHeConfirm;
		requiredFromOutside = true;
		initCardDialog(targetName, true, "GuoHe -- target: " + targetName);
	}

	public void shunShouConfirmInit(String targetName) {
		disableAllButtons();
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		myBattleField.requireClickableSquares();
		myLowerPanel.requireClickableCards();
		currentAction = Action.ShunShouConfirm;
		requiredFromOutside = true;
		initCardDialog(targetName, true, "ShunShou -- target: " + targetName);
	}

	private void guoHeConfirm(String targetName, Card targetCard) {
		myHero.guoHeConfirm(targetName, targetCard);
	}

	private void shunShouConfirm(String targetName, Card targetCard) {
		myHero.shunShouConfirm(targetName, targetCard);
	}

	private void jieDaoConfirm(String targetName, Card targetCard) {

	}

	public void shunShouProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.ShunShou;
		requiredFromOutside = false;
		myBattleField.setRequiredNum(1);
		myBattleField.setRequiredDistance(BattleField.horse_range);
		myBattleField.setRequireEmpty(false);
		myBattleField.myselfClickable = false;
		myBattleField.requireClickableSquares();
		enableCancel();
	}

	public void shunShouProcessEnd() {
		currentAction = null;
		Card ShunShouCard = myLowerPanel.chosenCards.get(0);
		myHero.playShunShou(ShunShouCard);
		myHero.shunShou(ShunShouCard,
				myBattleField.clickedSquares.get(0).occupier);
	}

	// images for identity
	static Image IdentityBack;

	static Image ZhuGong;
	static Image FanZei;
	static Image NeJian;
	static Image ZhongChen;
	// images for blood
	static Image Blood3;
	static Image Blood4;
	static Image Blood5;

	public void recSysMsg(String msg) {
		while (myRightPanel == null) {
			try {
				Thread.sleep(5);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		myRightPanel.appendSysMsg(msg);
	}

	public void recMessageMsg(String msg) {
		myRightPanel.appendMsg(msg);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#finalize()
	 */
	@Override
	protected void finalize() throws Throwable {
		// if (myModel.myLoginView.iamServer) {
		// myServer.quit();
		// } else {
		// myClient.quit();
		// }
		myModel.quit();
		super.finalize();
	}

	public void pandinChangeProcessInit() {
		if (myHero.canReplacePandin) {
			currentAction = Action.ReplacePandin;
		} else if (myHero.canChangePandin) {
			currentAction = Action.ChangePandin;
		} else {
			// THIS SHOULD NOT HAPPEN, server did a filter of those players who
			// can change pandin already
			// return;
			System.err
					.println("THIS SHOULD NEVER HAPPEN, check pandinChangeProcessInit() and its comments");
			// myModel.sendLine("G.N");
		}
		requiredFromOutside = true;
		// this is only set enabled after pandinChange card has been chosen
		// OK.setEnabled(true);
		// okEnabled = true;
		myBattleField.ready = true;
		myBattleField.requiredNumMin = 0;
		myBattleField.requiredNumMax = 0;
		myLowerPanel.pandinChangeReadyState();

		cancelEnabled = true;
		Cancel.setEnabled(true);
	}

	private void pandinChangeProcessEnd() {
		myHero.playCard(myLowerPanel.chosenCards.get(0));
		myHero.changePandin(myLowerPanel.chosenCards.get(0));
	}

	public void addProgressTo(String playerName) {
		System.out.println("addProgressTo() " + playerName);
		if (playerName.equalsIgnoreCase(myModel.myPlayerName)) {
			// IMPORTANT notice the parameter here
			// myHero.getLoc().addProgress(this);
			myHero.getLoc().addProgress();
			return;
		} else {
			for (Player every : myModel.playerList) {
				if (every.getName().equalsIgnoreCase(playerName)) {
					every.getCharacter().getLoc().addProgress();
					return;
				}
			}
		}
	}

	public void clearProgress() {
		// try {
		// Progress.progressLock.lock();
		// } catch (InterruptedException e1) {
		// // TODO Auto-generated catch block
		// e1.printStackTrace();
		// }

		// System.out.println("View.clearProgress()");
		for (Square[] every : myBattleField.squares) {
			for (Square everyone : every) {
				everyone.removeProgress();
			}
		}
		// Progress.progressLock.unlock();
		// System.out.println("View.clearProgress() done");
	}

	public void addProgressToAll() {
		System.out.println("addProgressToAll()");
		// myHero.getLoc().addProgress(this);
		for (Player every : myModel.playerList) {
			// if (!every.getName().equalsIgnoreCase(myModel.myPlayerName)) {
			every.getCharacter().getLoc().addProgress();
			// }
		}
	}

	HeroChoosingDialog myHeroDialog;

	public void initHeroDialog(List<String> heroNames) {
		myHeroDialog = new HeroChoosingDialog(this);
		myHeroDialog.setVisible(false);
		for (String every : heroNames) {
			try {
				Hero toBadd = (Hero) Class.forName("heroes." + every)
						.newInstance();
				toBadd.associate(myHeroDialog);
				// myModel.myLoginView.testMsg("Hero added: "+
				// toBadd.getName());
				// myModel.myLoginView.testMsg("image size is:"
				// +toBadd.getSize());
				System.out
						.println("Image info:" + toBadd.getImage().toString());
				System.out
						.println("Image is null? " + toBadd.getImage() == null);
				myHeroDialog.addHero(toBadd);
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
		}
		myHeroDialog.setResizable(false);
		myHeroDialog.answerable = true;
		myHeroDialog.setVisible(true);
	}

	/**
	 * this method is called when the player chose his hero, then this method
	 * will notify the server what hero we have chosen
	 * 
	 * @param toB
	 */
	public void heroChosen(Hero toB) {
		myHeroDialog.dispose();
		myHeroDialog = null;
		myHero = toB;
		myHero.setView(this);
		// this step will be done when the Client rec "S.BATTLEFIELD.HEROINFO",
		// that's when the playerList's heroes are all initialized. Don't worry
		// now.
		// myPlayer.setCharacter(myHero);
		// System.out.println("heroChosen() called");
		myModel.heroChosen(toB);
	}

	CardChoosingDialog myCardDialog;

	private void initCardDialog(String targetName, boolean equipAllowed,
			String dialogTitle) {
		System.out.println("View.initCardDialog() " + targetName + " , "
				+ equipAllowed);
		myCardDialog = new CardChoosingDialog(this);
		myCardDialog.setTitle(dialogTitle);
		myCardDialog.setTargetName(targetName);
		System.out.println("View.initCardDialog() 1");
		// get target Hero
		Hero targetHero = null;
		for (Player every : myModel.playerList) {
			System.out.println(every.getName());
			if (every.getName().equalsIgnoreCase(targetName)) {
				targetHero = every.getCharacter();
				System.out.println("initCardDialog() targetHero found.");
				break;
			}
		}
		System.out.println("View.initCardDialog() targetHero:" + targetHero);
		// add cards into the dialog.
		myCardDialog.addInvisibleCards(targetHero.handCardNum);
		if (equipAllowed) {
			if (targetHero.weaponEquipped) {
				myCardDialog.addCard(targetHero.weapon);
			}
			if (targetHero.armorEquipped) {
				myCardDialog.addCard(targetHero.armor);
			}
			if (targetHero.plusHorseEquipped) {
				myCardDialog.addCard(targetHero.plusHorse);
			}
			if (targetHero.minusHorseEquipped) {
				myCardDialog.addCard(targetHero.minusHorse);
			}
		}
		myCardDialog.setVisible(true);
	}

	private void initCardDialog(int cardNum, ArrayList<Card> cards) {
		System.out.println("View.initCardDialog() for WuGu");
		System.out.println(cardNum + ":" + cards);
		myCardDialog = new CardChoosingDialog(this);
		myCardDialog.setTitle("Wu Gu Feng Deng");
		myCardDialog.setTargetName(null);
		System.out.println("View.initCardDialog() before adding cards");
		for (int i = 0; i < cardNum; i++) {
			myCardDialog.addCard(cards.get(i));
		}
		System.out.println("View.initCardDialog() after adding cards");
		myCardDialog.setVisible(true);
	}

	/**
	 * this method is called after you choose the card in the process of:
	 * GuoHeConfirm, JieDaoConfirm,
	 * 
	 * @param every
	 */
	public void cardChosen(String targetName, Card targetCard) {
		if (requiredFromOutside == true) {
			switch (currentAction) {
			case GuoHeConfirm:
				guoHeConfirm(targetName, targetCard);
				break;
			case ShunShouConfirm:
				shunShouConfirm(targetName, targetCard);
				break;
			case WuGuConfirm:
				if (targetName != null) {
					System.err
							.println("WuGuConfirm targetName != null, should never happen");
				}
				wuGuConfirmEnd(targetCard);
				break;
			}
		} else {
			return;
		}
	}

	public void firstRound() {
		myRightPanel.currentPlayerBox.moveLeft();
		myModel.currentPlayer.getCharacter().getLoc().setActivated(true);
		disableAllButtons();
	}

	public void myHeroDead() {
		// TODO discard all the cards
		// disable all the buttons, except talk
	}

	public void playerDead(String targetName) {
		myRightPanel.playerDead(targetName);
		for (Player every : myModel.playerList) {
			if (every.getName().equalsIgnoreCase(targetName)) {
				every.getCharacter().getLoc().occupierRemove();
				break;
			}
		}
	}

	public void saveProcessInit(int healNumNeeded, boolean spiritUsable) {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.Save;
		enableCancel();
		requiredFromOutside = true;
		myBattleField.ready = true;
		myLowerPanel.setRequiredNum(1, healNumNeeded);
		myLowerPanel.requireFromHand = true;
		myLowerPanel.cardRequired = true;
		myLowerPanel.cardRequiredString.add("Heal");
		// TODO should also add "Spirit" if it's self-save
		if (spiritUsable) {
			myLowerPanel.cardRequiredString.add("Spirit");
		}
		myLowerPanel.requireClickableCards();
	}

	public void saveProcessEnd() {
		currentAction = null;
		int healNumPlayed = myLowerPanel.chosenCards.size();
		for (int i = 0; i < healNumPlayed; i++) {
			myHero.playHeal(myLowerPanel.chosenCards.get(i));
		}
		myHero.save(healNumPlayed);
		// this will not tell the server that I finish save,
		// maybe the player wants to continue play "Heal"
		// so the player still has control, until he clicks cancel, or 30 sec
		// passes
	}

	public void drinkSpiritProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.DrinkSpirit;
		requiredFromOutside = false;
		myBattleField.ready = true;
		cancelEnabled = true;
		Cancel.setEnabled(true);
	}

	public void drinkSpiritProcessEnd() {
		currentAction = null;
		myHero.playSpirit(myLowerPanel.chosenCards.get(0));
		myHero.drinkSpirit();
	}

	public void BinLiangProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.BinLiang;
		requiredFromOutside = false;
		myBattleField.setRequiredNum(1);
		myBattleField.setRequiredDistance(BattleField.horse_range);
		myBattleField.setRequireEmpty(false);
		myBattleField.myselfClickable = false;
		myBattleField.requireClickableSquares();
		cancelEnabled = true;
		Cancel.setEnabled(true);
	}

	private void BinLiangProcessEnd() {
		currentAction = null;
		Card BinLiangCard = myLowerPanel.chosenCards.get(0);
		myHero.playBinLiang(BinLiangCard);
		myHero.binLiang(BinLiangCard,
				myBattleField.clickedSquares.get(0).occupier);
	}

	public void LeBuProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.LeBu;
		requiredFromOutside = false;
		myBattleField.setRequiredNum(1);
		myBattleField.setRequiredDistance(BattleField.any_range);
		myBattleField.setRequireEmpty(false);
		myBattleField.myselfClickable = false;
		myBattleField.requireClickableSquares();
		cancelEnabled = true;
		Cancel.setEnabled(true);
	}

	private void LeBuProcessEnd() {
		currentAction = null;
		Card LeBuCard = myLowerPanel.chosenCards.get(0);
		myHero.playLeBu(LeBuCard);
		myHero.leBu(LeBuCard, myBattleField.clickedSquares.get(0).occupier);
	}

	public void ShanDianProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.ShanDian;
		requiredFromOutside = false;
		myBattleField.ready = true;
		cancelEnabled = true;
		Cancel.setEnabled(true);
		// LowerPanel.notifyView() will be called after this method is called.
	}

	private void ShanDianProcessEnd() {
		currentAction = null;
		Card ShanDianCard = myLowerPanel.chosenCards.get(0);
		myHero.playShanDian(ShanDianCard);
		myHero.shanDian(ShanDianCard);
	}

	public void healProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.Heal;
		requiredFromOutside = false;
		myBattleField.ready = true;
		enableCancel();
	}

	private void healProcessEnd() {
		currentAction = null;
		Card HealCard = myLowerPanel.chosenCards.get(0);
		myHero.playHeal(HealCard);
		myHero.heal(HealCard, myHero);
	}

	public void getHandCard(Card toBget) {
		myLowerPanel.myLowerCardContainer.addCard(toBget);
		validate();
		repaint();
	}

	public void loseHandCard(Card toBremoved) {
		myLowerPanel.myLowerCardContainer.removeCard(toBremoved);
	}

	public void equipWeaponProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.EquipWeapon;
		requiredFromOutside = false;
		myBattleField.ready = true;
		enableCancel();
	}

	public void equipWeaponProcessEnd() {
		currentAction = null;
		Card equip = myLowerPanel.chosenCards.get(0);
		myHero.playWeapon(equip);
		myModel.equipWeapon(equip);
	}

	public void equipArmorProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.EquipArmor;
		requiredFromOutside = false;
		myBattleField.ready = true;
		enableCancel();
	}

	public void equipArmorProcessEnd() {
		currentAction = null;
		Card equip = myLowerPanel.chosenCards.get(0);
		myHero.playArmor(equip);
		myModel.equipArmor(equip);
	}

	public void equipHorsePlusProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.EquipHorsePlus;
		myBattleField.ready = true;
		requiredFromOutside = false;
		enableCancel();
	}

	public void equipHorsePlusProcessEnd() {
		currentAction = null;
		Card equip = myLowerPanel.chosenCards.get(0);
		myHero.playHorsePlus(equip);
		myModel.equipHorsePlus(equip);
	}

	public void equipHorseMinusProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.EquipHorseMinus;
		myBattleField.ready = true;
		requiredFromOutside = false;
		enableCancel();
	}

	public void equipHorseMinusProcessEnd() {
		currentAction = null;
		Card equip = myLowerPanel.chosenCards.get(0);
		myHero.playHorseMinus(equip);
		myModel.equipHorseMinus(equip);
	}

	public void wuGuProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.WuGu;
		requiredFromOutside = false;
		myBattleField.ready = true;
		enableCancel();
	}

	private void wuGuProcessEnd() {
		currentAction = null;
		Card wuGuCard = myLowerPanel.chosenCards.get(0);
		myHero.playWuGu(wuGuCard);
		myHero.wuGu();
	}

	public void wuGuConfirmInit(int cardNum, ArrayList<Card> cards) {
		disableAllButtons();
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		myBattleField.requireClickableSquares();
		myLowerPanel.requireClickableCards();
		currentAction = Action.WuGuConfirm;
		requiredFromOutside = true;
		initCardDialog(cardNum, cards);
	}

	private void wuGuConfirmEnd(Card targetCard) {
		myHero.wuGuConfirm(targetCard);
	}

	public void wuZhongProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.WuZhong;
		requiredFromOutside = false;
		myBattleField.ready = true;
		enableCancel();
	}

	private void wuZhongProcessEnd() {
		currentAction = null;
		Card wuZhongCard = myLowerPanel.chosenCards.get(0);
		myHero.playWuZhong(wuZhongCard);
		myHero.wuZhong();
	}

	public void wanJianProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.WanJian;
		requiredFromOutside = false;
		myBattleField.ready = true;
		enableCancel();
	}

	public void wanJianProcessEnd() {
		currentAction = null;
		Card wanJianCard = myLowerPanel.chosenCards.get(0);
		myHero.playWanJian(wanJianCard);
		myHero.wanJian();
	}

	public void nanManProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.NanMan;
		requiredFromOutside = false;
		myBattleField.ready = true;
		enableCancel();
	}

	public void nanManProcessEnd() {
		currentAction = null;
		Card nanManCard = myLowerPanel.chosenCards.get(0);
		myHero.playNanMan(nanManCard);
		myHero.nanMan();
	}

	public void nanManConfirmInit() {
		disableOK();
		disableEnd();
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		myBattleField.ready = true;
		requiredFromOutside = true;
		enableCancel();
		currentAction = Action.NanManConfirm;
		myLowerPanel.setRequiredNum(1);
		myLowerPanel.ready = false;
		myLowerPanel.requireFromHand = true;
		myLowerPanel.cardRequiredString.add("Attack");
		myLowerPanel.cardRequiredString.add("FireAttack");
		myLowerPanel.cardRequiredString.add("ThunderAttack");
		myLowerPanel.cardRequired = true;
		myLowerPanel.requireClickableCards();
	}

	public void nanManConfirmEnd() {
		currentAction = null;
		Card attackCard = myLowerPanel.chosenCards.get(0);
		myHero.playAttack(attackCard);
		myHero.nanManConfirm();
	}

	public void taoYuanProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.TaoYuan;
		requiredFromOutside = false;
		myBattleField.ready = true;
		enableCancel();
	}

	public void taoYuanProcessEnd() {
		currentAction = null;
		Card taoYuanCard = myLowerPanel.chosenCards.get(0);
		myHero.playTaoYuan(taoYuanCard);
		myHero.taoYuan();
	}

	public void jieDaoProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		// attackCard = card;
		currentAction = Action.JieDao;
		requiredFromOutside = false;
		myBattleField.setRequiredNum(1);
		myBattleField.setRequiredDistance(BattleField.any_range);
		myBattleField.setRequireEmpty(false);
		myBattleField.ifrequireWeapon = true;
		myBattleField.myselfClickable = false;
		myBattleField.requireClickableSquares();
		enableCancel();
	}

	public void jieDaoProcessEnd() {
		currentAction = null;
		Card jieDaoCard = myLowerPanel.chosenCards.get(0);
		myHero.playJieDao(jieDaoCard);
		myHero.jieDao(myBattleField.clickedSquares.get(0).occupier
				.getController());
	}

	/**
	 * choose the attacking target of JieDao.
	 */
	public void jieDaoProcessInit2(String answereeName) {
		System.out.println("jieDaoProcessInit2() weapon range depends on "
				+ answereeName);
		myBattleField.clearClicked();
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		myLowerPanel.ready = true;
		myBattleField.inJieDao2process = true;
		myBattleField.jieDaoTarget1 = answereeName;
		// attackCard = card;
		currentAction = Action.JieDao2;
		requiredFromOutside = false;
		myBattleField.setRequiredNum(1);

		// cannot use this, need a new method for JieDao
		myBattleField.requireJieDaoRangeFor(answereeName);
		// myBattleField.setRequiredDistance(BattleField.any_range);
		// myBattleField.setRequireEmpty(false);
		// myBattleField.myselfClickable = true;
		// myBattleField.requireClickableSquares();
		enableCancel();
	}

	public void jieDaoProcessEnd2() {
		currentAction = null;
		myHero.jieDao2(myBattleField.clickedSquares.get(0).occupier
				.getController());
		myBattleField.inJieDao2process = false;
		myBattleField.jieDaoTarget1 = null;
	}

	String jieDaoTarget2 = null;

	public void jieDaoConfirmProcessInit(String jieDaoSource,
			String jieDaoTarget2) {
		disableOK();
		disableEnd();
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		myBattleField.ready = true;
		requiredFromOutside = true;
		enableCancel();
		myLowerPanel.setRequiredNum(1);
		myLowerPanel.ready = false;
		myLowerPanel.requireFromHand = true;
		myLowerPanel.cardRequiredString.add("Attack");
		myLowerPanel.cardRequiredString.add("FireAttack");
		myLowerPanel.cardRequiredString.add("ThunderAttack");
		myLowerPanel.cardRequired = true;
		myLowerPanel.requireClickableCards();
		currentAction = Action.JieDaoConfirm;
		this.jieDaoTarget2 = jieDaoTarget2;
		recSysMsg(jieDaoSource + " forces you to Attack " + jieDaoTarget2
				+ " or to give him your weapon. Your choice is?");
	}

	public void jieDaoConfirmProcessEnd() {
		currentAction = null;
		Card jieDaoAttack = myLowerPanel.chosenCards.get(0);
		myHero.playAttack(jieDaoAttack);
		Hero targetHero = null;
		for (Player every : myModel.playerList) {
			if (every.getName().equalsIgnoreCase(jieDaoTarget2)) {
				targetHero = every.getCharacter();
				break;
			}
		}
		myHero.jieDaoConfirm((Basic) myLowerPanel.chosenCards.get(0),
				targetHero);
	}

	public void discardConfirmed(Card every) {
		myLowerPanel.discardConfirmed(every);
	}

	public void tieSuoProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.TieSuo;
		requiredFromOutside = false;
		myBattleField.setRequiredNum(1, 2);

		myBattleField.setRequiredDistance(BattleField.any_range);
		myBattleField.setRequireEmpty(false);
		myBattleField.myselfClickable = true;
		myBattleField.requireClickableSquares();
		enableCancel();
	}

	public void tieSuoProcessEnd() {
		currentAction = null;
		Card tieSuoCard = myLowerPanel.chosenCards.get(0);
		myHero.playTieSuo(tieSuoCard);
		ArrayList<Hero> targets = new ArrayList<Hero>();
		for (int i = 0; i < myBattleField.clickedSquares.size(); i++) {
			targets.add(myBattleField.clickedSquares.get(i).occupier);
		}
		myHero.tieSuo(tieSuoCard, targets);
	}

	public void huoGongProcessInit() {
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.HuoGong;
		myBattleField.setRequiredNum(1);
		myBattleField.setRequiredDistance(BattleField.any_range);
		myBattleField.setRequireEmpty(false);
		myBattleField.myselfClickable = true;
		myBattleField.requireClickableSquares();
		enableCancel();
	}

	public void huoGongProcessEnd() {
		currentAction = null;
		Card huoGongCard = myLowerPanel.chosenCards.get(0);
		myHero.playHuoGong(huoGongCard);
		myHero.huoGong(huoGongCard,
				myBattleField.clickedSquares.get(0).occupier);
	}

	String huoGongStarterName = null;

	public void huoGongConfirm1Init(String HuoGongStarterName) {
		huoGongStarterName = HuoGongStarterName;
		recSysMsg("You need to show one card.");
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.HuoGongConfirm1;
		requiredFromOutside = true;
		myBattleField.ready = true;
		// enableCancel();
		myLowerPanel.setRequiredNum(1);
		myLowerPanel.requireFromEquip = false;
		myLowerPanel.requireFromHand = true;
		myLowerPanel.requireClickableCards();
	}

	public void huoGongConfirm1End() {
		currentAction = null;
		Card huoGong1Card = myLowerPanel.chosenCards.get(0);
		myHero.huoGongConfirm1(huoGong1Card, huoGongStarterName);
		huoGongStarterName = null;
	}

	String huogongTargetName = null;

	public void huoGongConfirm2Init(String color, String suit, String targetname) {
		recSysMsg("You need to show a card of suit " + suit);
		huogongTargetName = targetname;
		int colorInt = Integer.parseInt(color);
		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		currentAction = Action.HuoGongConfirm2;
		requiredFromOutside = true;
		myBattleField.ready = true;
		enableCancel();
		myLowerPanel.colorRequired = true;
		myLowerPanel.colorRequiredInt.add(colorInt);
		myLowerPanel.requireFromEquip = false;
		myLowerPanel.requireFromHand = true;
		myLowerPanel.setRequiredNum(1);
		myLowerPanel.requireClickableCards();
	}

	public void huoGongConfirm2End() {
		currentAction = null;
		Card huoGong2Card = myLowerPanel.chosenCards.get(0);
		ArrayList<Card> newOne = new ArrayList<Card>();
		newOne.add(huoGong2Card);
		myHero.discardCard(newOne);
		myHero.huoGongConfirm2(huoGong2Card, huogongTargetName);
		huogongTargetName = null;
	}

	public void baGuaProcessInit() {

		myBattleField.clearRequirements();
		myLowerPanel.clearRequirements();
		requiredFromOutside = true;
		myBattleField.ready = true;
		myLowerPanel.ready = true;
		currentAction = Action.BaGua;
		enableCancel();
		checkReady();
	}

	public void baGuaProcessEnd() {
		currentAction = null;
		myHero.baGua();
	}

}
